#include "octreeNode.h"
#include <cmath>
#include <GL/gl.h>
#include "defines/operation.h"
#include "defines/rotation.h"
#define COMMENT_RESULT false
#define COMMENT_STEP false
#define COMMENT_FINAL_RESULT false

OctreeNode::OctreeNode(int _depth, int _position, OctreeNode *_parent, unsigned int _type) : depth(_depth), position(_position), parent(_parent), type(_type){
    node = new OctreeNode*[8];

    for(int i = 0; i < 8; i++){
        node[i] = NULL;
    }
}

OctreeNode::OctreeNode(const OctreeNode &_node, OctreeNode *_parent, bool _inverse){
    type = _node.getType();
    depth = _node.getDepth();
    position = _node.getPosition();
    parent = _parent;
    if(_inverse){
        if (type == BLACK) type = WHITE;
        else if (type == WHITE) type = BLACK;
    }
    node = new OctreeNode*[8];

    for(int i = 0; i < 8; i++){
        if(type == GREY){
            node[i] = new OctreeNode(*_node.getNode(i), this, _inverse);
        }else{
            node[i] = NULL;
        }
    }
}

OctreeNode::~OctreeNode(){
    for (int i = 0; i < 8; i++){
        if ( node[i] ){
            delete node[i];
        }
    }
    delete [] node;
}

unsigned int OctreeNode::getType() const{
    return type;
}

int OctreeNode::getDepth() const{
    return depth;
}

int OctreeNode::getPosition() const{
    return position;
}

void OctreeNode::setPosition(int _p){
    position = _p;
}

OctreeNode *OctreeNode::getParent() const{
    return parent;
}

OctreeNode *OctreeNode::getNode(int _i) const{
    return node[_i];
}

void OctreeNode::setNode(int _i, OctreeNode *_node){
    node[_i] = _node;
}

void OctreeNode::getAllPoints(Vector3D *_point, const Vector3D &_min, const Vector3D &_max){
    _point[0] = _min;
    _point[1] = Vector3D(_max.x, _min.y, _min.z);
    _point[2] = Vector3D(_min.x, _min.y, _max.z);
    _point[3] = Vector3D(_max.x, _min.y, _max.z);
    _point[4] = Vector3D(_min.x, _max.y, _min.z);
    _point[5] = Vector3D(_max.x, _max.y, _min.z);
    _point[6] = Vector3D(_min.x, _max.y, _max.z);
    _point[7] = _max;
}

void OctreeNode::createStatic(char _type){
    if(_type == '('){
        type = GREY;

        node[0] = new OctreeNode(depth + 1, 0, this);

        node[1] = new OctreeNode(depth + 1, 1, this);

        node[2] = new OctreeNode(depth + 1, 2, this);;

        node[3] = new OctreeNode(depth + 1, 3, this);

        node[4] = new OctreeNode(depth + 1, 4, this);

        node[5] = new OctreeNode(depth + 1, 5, this);

        node[6] = new OctreeNode(depth + 1, 6, this);

        node[7] = new OctreeNode(depth + 1, 7, this);
    }else if(_type == 'W'){
        type = WHITE;
    }else if(_type == 'B'){
        type = BLACK;
    }
}

void OctreeNode::create(Object *_obj, int _depth, const Vector3D &_min, const Vector3D &_max){
    Vector3D point[8];
    getAllPoints(point, _min, _max);
    type = _obj->classify(point);

    if(type == GREY){
        if(_depth == 0){
            type = BLACK;
        }else{
            node[0] = new OctreeNode(depth + 1, 0, this);

            node[1] = new OctreeNode(depth + 1, 1, this);

            node[2] = new OctreeNode(depth + 1, 2, this);;

            node[3] = new OctreeNode(depth + 1, 3, this);

            node[4] = new OctreeNode(depth + 1, 4, this);

            node[5] = new OctreeNode(depth + 1, 5, this);

            node[6] = new OctreeNode(depth + 1, 6, this);

            node[7] = new OctreeNode(depth + 1, 7, this);

            for(int i = 0; i < 8; i++){
                Vector3D childMin, childMax;
                getChildMinMax(i, point, &childMin, &childMax);
                node[i]->create(_obj, _depth - 1, childMin, childMax);
            }
        }
    }
}
/*
         p6------p7 max
         /|      /|
        p2------P3|
        |P4-----|-p5
        |/      |/
    min p0------p1
    z
    |  y
    |/_ _ x

*/

void OctreeNode::getChildMinMax(int _position, Vector3D *_point, Vector3D *_childMin, Vector3D *_childMax) const{
    switch(_position){
        case 0:{
            *_childMin = _point[0];
            *_childMax = (_point[0] + _point[7]) * (1.0/2.0);
            break;
        }
        case 1:{
            *_childMin = (_point[0] + _point[1]) * (1.0/2.0);
            *_childMax = (_point[1] + _point[7]) * (1.0/2.0);
            break;
        }
        case 2:{
            *_childMin = (_point[0] + _point[2]) * (1.0/2.0);
            *_childMax = (_point[2] + _point[7]) * (1.0/2.0);
            break;
        }
        case 3:{
            *_childMin = (_point[0] + _point[3]) * (1.0/2.0);
            *_childMax = (_point[3] + _point[7]) * (1.0/2.0);
            break;
        }
        case 4:{
            *_childMin = (_point[0] + _point[4]) * (1.0/2.0);
            *_childMax = (_point[4] + _point[7]) * (1.0/2.0);
            break;
        }
        case 5:{
            *_childMin = (_point[0] + _point[5]) * (1.0/2.0);
            *_childMax = (_point[5] + _point[7]) * (1.0/2.0);
            break;
        }
        case 6:{
            *_childMin = (_point[0] + _point[6]) * (1.0/2.0);
            *_childMax = (_point[6] + _point[7]) * (1.0/2.0);
            break;
        }
        case 7:{
            *_childMin = (_point[0] + _point[7]) * (1.0/2.0);
            *_childMax = _point[7];
            break;
        }
    }
}

void OctreeNode::draw(bool _wireframe, bool _border, Color _color, const Vector3D &_min, const Vector3D &_max){
    Vector3D point[8];
    getAllPoints(point, _min, _max);

    if(_border) drawNode(_border, Color(_color.red, _color.green, _color.blue, 0.5*_color.alpha), point);
    if(type == BLACK){
        if((_border && _wireframe)){
            glLineWidth(2);
            drawNode(_wireframe, _color, point);
            glLineWidth(1);
        }else{
            drawNode(_wireframe, _color, point);
        }
    }else if(type == GREY){
        for(int i = 0; i < 8; i++){
            Vector3D childMin, childMax;
            getChildMinMax(i, point, &childMin, &childMax);
            node[i]->draw(_wireframe, _border, _color, childMin, childMax);
        }
    }
}

void OctreeNode::drawNode(bool _wireframe, Color _color, Vector3D *_point){
    if(_wireframe){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glDisable(GL_CULL_FACE);
    }

    Vector3D normal;
    glBegin(GL_QUADS);

        glColor4d(_color.red, _color.green, _color.blue, _color.alpha);

    //parte de baixo
        normal = ((_point[4] - _point[0]).crossProduct(_point[1] - _point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(_point[0].x, _point[0].y, _point[0].z);
        glVertex3f(_point[4].x, _point[4].y, _point[4].z);
        glVertex3f(_point[5].x, _point[5].y, _point[5].z);
        glVertex3f(_point[1].x, _point[1].y, _point[1].z);


    //parte de cima
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(_point[2].x, _point[2].y, _point[2].z);
        glVertex3f(_point[3].x, _point[3].y, _point[3].z);
        glVertex3f(_point[7].x, _point[7].y, _point[7].z);
        glVertex3f(_point[6].x, _point[6].y, _point[6].z);

    //parte de esquerda
        normal = ((_point[2] - _point[0]).crossProduct(_point[4] - _point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(_point[0].x, _point[0].y, _point[0].z);
        glVertex3f(_point[2].x, _point[2].y, _point[2].z);
        glVertex3f(_point[6].x, _point[6].y, _point[6].z);
        glVertex3f(_point[4].x, _point[4].y, _point[4].z);

    //parte de direita
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(_point[1].x, _point[1].y, _point[1].z);
        glVertex3f(_point[5].x, _point[5].y, _point[5].z);
        glVertex3f(_point[7].x, _point[7].y, _point[7].z);
        glVertex3f(_point[3].x, _point[3].y, _point[3].z);

    //parte de frente
        normal = ((_point[1] - _point[0]).crossProduct(_point[2] - _point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3f(_point[0].x, _point[0].y, _point[0].z);
        glVertex3f(_point[1].x, _point[1].y, _point[1].z);
        glVertex3f(_point[3].x, _point[3].y, _point[3].z);
        glVertex3f(_point[2].x, _point[2].y, _point[2].z);

    //parte de tras
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3f(_point[4].x, _point[4].y, _point[4].z);
        glVertex3f(_point[6].x, _point[6].y, _point[6].z);
        glVertex3f(_point[7].x, _point[7].y, _point[7].z);
        glVertex3f(_point[5].x, _point[5].y, _point[5].z);
    glEnd();


    if(_wireframe){
        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glEnable(GL_CULL_FACE);
    }

}

double OctreeNode::volume() const{
    //MODO DE FAZER: cada vez que vai criando a octree ela ja volta o volume dela
    if(type == BLACK){
        return pow(1.0/8.0, depth);
    }else if(type == WHITE){
        return 0;
    }

    double result = 0;
    for(int i = 0; i < 8; i++){
        result += node[i]->volume();
    }
    return result;
}

double OctreeNode::area(){
    if(type == BLACK){
        if(COMMENT_STEP) std::cout <<"É preto em pos: " << position << " | prof: " << depth <<std::endl;
        if(parent){
            std::stack<int> stack;
            stack.push(position);
            double b = parent->getWhiteNeighbours(depth, stack);
//            int nhe;
  //          std::cin >> nhe;

            return pow(1.0/4.0, depth) * b;
        }else{
            if(COMMENT_RESULT) std::cout << "retorntando * 6" <<std::endl;
            return pow(1.0/4.0, depth) * 6;
        }
    }else if(type == GREY){
        if(COMMENT_STEP) std::cout <<"É cinza em pos: " << position << " | prof: " << depth <<std::endl;
        double result = 0;
        for(int i = 0; i < 8; i++){
            result += node[i]->area();
        }
//        std::cout << "\tretorntando: " << result <<std::endl;
        return result;
    }

    return 0;
}

double OctreeNode::getWhiteNeighbours(int _nodeDepth, std::stack<int> _stack){
    double result = 0;

    int top = _stack.top();
    _stack.push(position);

    if(COMMENT_STEP) std::cout <<"O pai em " << position << "|" << depth<< " está procurando o filho de caso: " << top << " e profundidade " << _nodeDepth <<std::endl;
    switch(top){
        case 0:{
            if(!parent){//se for root
                //baixo
                result += 1;

                //esquerdo
                result += 1;

                //frente
                result += 1;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                //baixo
                result += parent->getWhiteNeighboursDown(_nodeDepth, _stack);

                //esquerdo
                result += parent->getWhiteNeighboursLeft(_nodeDepth, _stack);

                //frente
                result += parent->getWhiteNeighboursFront(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            //cima
            result += node[2]->getWhiteChildDown(_nodeDepth, _stack);

            //direito
            result += node[1]->getWhiteChildLeft(_nodeDepth, _stack);

            //tras
            result += node[4]->getWhiteChildFront(_nodeDepth, _stack);
            break;
        }
        case 1:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursDown(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursRight(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursFront(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[3]->getWhiteChildDown(_nodeDepth, _stack);

            result += node[0]->getWhiteChildRight(_nodeDepth, _stack);

            result += node[5]->getWhiteChildFront(_nodeDepth, _stack);
            break;
        }
        case 2:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursUp(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursLeft(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursFront(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[0]->getWhiteChildUp(_nodeDepth, _stack);

            result += node[3]->getWhiteChildLeft(_nodeDepth, _stack);

            result += node[6]->getWhiteChildFront(_nodeDepth, _stack);
            break;
        }
        case 3:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursUp(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursRight(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursFront(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[1]->getWhiteChildUp(_nodeDepth, _stack);

            result += node[2]->getWhiteChildRight(_nodeDepth, _stack);

            result += node[7]->getWhiteChildFront(_nodeDepth, _stack);
            break;
        }
        case 4:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursDown(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursLeft(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursBack(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[6]->getWhiteChildDown(_nodeDepth, _stack);

            result += node[5]->getWhiteChildLeft(_nodeDepth, _stack);

            result += node[0]->getWhiteChildBack(_nodeDepth, _stack);
            break;
        }
        case 5:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursDown(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursRight(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursBack(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[7]->getWhiteChildDown(_nodeDepth, _stack);

            result += node[4]->getWhiteChildRight(_nodeDepth, _stack);

            result += node[1]->getWhiteChildBack(_nodeDepth, _stack);
            break;
        }
        case 6:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursUp(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursLeft(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursBack(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[4]->getWhiteChildUp(_nodeDepth, _stack);

            result += node[7]->getWhiteChildLeft(_nodeDepth, _stack);

            result += node[2]->getWhiteChildBack(_nodeDepth, _stack);
            break;
        }
        case 7:{
            if(!parent){//se for root
                result += 3;
                if(COMMENT_STEP) std::cout << "SEM PAI - somando 3" <<std::endl;
            }else{
                result += parent->getWhiteNeighboursUp(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursRight(_nodeDepth, _stack);

                result += parent->getWhiteNeighboursBack(_nodeDepth, _stack);
            }

            _stack.pop();
            _stack.pop();
            result += node[5]->getWhiteChildUp(_nodeDepth, _stack);

            result += node[6]->getWhiteChildRight(_nodeDepth, _stack);

            result += node[3]->getWhiteChildBack(_nodeDepth, _stack);

            break;
        }
        default:{
            std::cout <<"Não foi achado o no filho!" <<std::endl;
            break;
        }
    }

    if(COMMENT_FINAL_RESULT) std::cout << "\tretornando total: " << result << std::endl << std::endl;
    return result;
}

double OctreeNode::getWhiteChildUp(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildUp : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[2]->getWhiteChildUp(_nodeDepth, _stack);
            result += node[3]->getWhiteChildUp(_nodeDepth, _stack);
            result += node[6]->getWhiteChildUp(_nodeDepth, _stack);
            result += node[7]->getWhiteChildUp(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 0:{
                    result += node[2]->getWhiteChildUp(_nodeDepth, _stack);
                    break;
                }
                case 1:{
                    result += node[3]->getWhiteChildUp(_nodeDepth, _stack);
                    break;
                }
                case 4:{
                    result += node[6]->getWhiteChildUp(_nodeDepth, _stack);
                    break;
                }
                case 5:{
                    result += node[7]->getWhiteChildUp(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildUp" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteChildDown(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildDown : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[0]->getWhiteChildDown(_nodeDepth, _stack);
            result += node[1]->getWhiteChildDown(_nodeDepth, _stack);
            result += node[4]->getWhiteChildDown(_nodeDepth, _stack);
            result += node[5]->getWhiteChildDown(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 2:{
                    result += node[0]->getWhiteChildDown(_nodeDepth, _stack);
                    break;
                }
                case 3:{
                    result += node[1]->getWhiteChildDown(_nodeDepth, _stack);
                    break;
                }
                case 6:{
                    result += node[4]->getWhiteChildDown(_nodeDepth, _stack);
                    break;
                }
                case 7:{
                    result += node[5]->getWhiteChildDown(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildDown" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteChildRight(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildRight : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[1]->getWhiteChildRight(_nodeDepth, _stack);
            result += node[5]->getWhiteChildRight(_nodeDepth, _stack);
            result += node[3]->getWhiteChildRight(_nodeDepth, _stack);
            result += node[7]->getWhiteChildRight(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 0:{
                    result += node[1]->getWhiteChildRight(_nodeDepth, _stack);
                    break;
                }
                case 4:{
                    result += node[5]->getWhiteChildRight(_nodeDepth, _stack);
                    break;
                }
                case 2:{
                    result += node[3]->getWhiteChildRight(_nodeDepth, _stack);
                    break;
                }
                case 6:{
                    result += node[7]->getWhiteChildRight(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildRight" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteChildLeft(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildLeft : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[0]->getWhiteChildLeft(_nodeDepth, _stack);
            result += node[4]->getWhiteChildLeft(_nodeDepth, _stack);
            result += node[2]->getWhiteChildLeft(_nodeDepth, _stack);
            result += node[6]->getWhiteChildLeft(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 1:{
                    result += node[0]->getWhiteChildLeft(_nodeDepth, _stack);
                    break;
                }
                case 5:{
                    result += node[4]->getWhiteChildLeft(_nodeDepth, _stack);
                    break;
                }
                case 3:{
                    result += node[2]->getWhiteChildLeft(_nodeDepth, _stack);
                    break;
                }
                case 7:{
                    result += node[6]->getWhiteChildLeft(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildLeft" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteChildFront(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildFront : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[0]->getWhiteChildFront(_nodeDepth, _stack);
            result += node[1]->getWhiteChildFront(_nodeDepth, _stack);
            result += node[2]->getWhiteChildFront(_nodeDepth, _stack);
            result += node[3]->getWhiteChildFront(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 4:{
                    result += node[0]->getWhiteChildFront(_nodeDepth, _stack);
                    break;
                }
                case 5:{
                    result += node[1]->getWhiteChildFront(_nodeDepth, _stack);
                    break;
                }
                case 6:{
                    result += node[2]->getWhiteChildFront(_nodeDepth, _stack);
                    break;
                }
                case 7:{
                    result += node[3]->getWhiteChildFront(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildFront" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteChildBack(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteChildBack : " <<std::endl;
    if(type == WHITE){
        int differenceDepth = depth - _nodeDepth;
        if(differenceDepth <= 0){
            if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
            return 1;
        }

        double b = pow(1.0/4.0, differenceDepth);
        if(COMMENT_RESULT) std::cout << "\tretornando menor: " << b << std::endl << std::endl;
        return b;
    }else if(type == GREY){
        double result = 0;
        if(_stack.empty()){
            result += node[4]->getWhiteChildBack(_nodeDepth, _stack);
            result += node[5]->getWhiteChildBack(_nodeDepth, _stack);
            result += node[6]->getWhiteChildBack(_nodeDepth, _stack);
            result += node[7]->getWhiteChildBack(_nodeDepth, _stack);
        }else{
            int top = _stack.top();
            _stack.pop();

            switch(top){
                case 0:{
                    result += node[4]->getWhiteChildBack(_nodeDepth, _stack);
                    break;
                }
                case 1:{
                    result += node[5]->getWhiteChildBack(_nodeDepth, _stack);
                    break;
                }
                case 2:{
                    result += node[6]->getWhiteChildBack(_nodeDepth, _stack);
                    break;
                }
                case 3:{
                    result += node[7]->getWhiteChildBack(_nodeDepth, _stack);
                    break;
                }
                default:{
                    std::cout <<"ERRO no getWhiteChildBack" <<std::endl;
                    break;
                }
            }
        }
        if(COMMENT_RESULT) std::cout << "\tretornando: " << result << std::endl << std::endl;
        return result;
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursUp(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursUp : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 2:
        case 3:
        case 6:
        case 7:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursUp(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 0:{
            _stack.pop();
            double b = node[2]->getWhiteChildDown(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 1:{
            _stack.pop();
            double b = node[3]->getWhiteChildDown(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 4:{
            _stack.pop();
            double b = node[6]->getWhiteChildDown(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 5:{
            _stack.pop();
            double b = node[7]->getWhiteChildDown(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursDown(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursDown : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 0:
        case 1:
        case 4:
        case 5:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursDown(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 2:{
            _stack.pop();
            double b = node[0]->getWhiteChildUp(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 3:{
            _stack.pop();
            double b = node[1]->getWhiteChildUp(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 6:{
            _stack.pop();
            double b = node[4]->getWhiteChildUp(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 7:{
            _stack.pop();
            double b = node[5]->getWhiteChildUp(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursRight(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursRight : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 1:
        case 3:
        case 5:
        case 7:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursRight(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 0:{
            _stack.pop();
            double b = node[1]->getWhiteChildLeft(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 2:{
            _stack.pop();
            double b = node[3]->getWhiteChildLeft(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 4:{
            _stack.pop();
            double b = node[5]->getWhiteChildLeft(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 6:{
            _stack.pop();
            double b = node[7]->getWhiteChildLeft(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursLeft(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursLeft : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 0:
        case 2:
        case 4:
        case 6:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursLeft(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 1:{
            _stack.pop();
            double b = node[0]->getWhiteChildRight(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 3:{
            _stack.pop();
            double b = node[2]->getWhiteChildRight(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 5:{
            _stack.pop();
            double b = node[4]->getWhiteChildRight(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 7:{
            _stack.pop();
            double b = node[6]->getWhiteChildRight(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursFront(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursFront : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 0:
        case 1:
        case 2:
        case 3:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursFront(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 4:{
            _stack.pop();
            double b = node[0]->getWhiteChildBack(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 5:{
            _stack.pop();
            double b = node[1]->getWhiteChildBack(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 6:{
            _stack.pop();
            double b = node[2]->getWhiteChildBack(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 7:{
            _stack.pop();
            double b = node[3]->getWhiteChildBack(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

double OctreeNode::getWhiteNeighboursBack(int _nodeDepth, std::stack<int> _stack){
    if(COMMENT_STEP) std::cout <<position<<"|" <<depth<<" - Entrando no getWhiteNeighboursBack : " <<std::endl;

    int top = _stack.top();
    switch(top){
        case 4:
        case 5:
        case 6:
        case 7:{
            if(!parent){
                if(COMMENT_RESULT) std::cout << "\tretornando: " << 1 << std::endl << std::endl;
                return 1;
            }
            _stack.push(position);
            double b = parent->getWhiteNeighboursBack(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 0:{
            _stack.pop();
            double b = node[4]->getWhiteChildFront(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 1:{
            _stack.pop();
            double b = node[5]->getWhiteChildFront(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 2:{
            _stack.pop();
            double b = node[6]->getWhiteChildFront(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
        case 3:{
            _stack.pop();
            double b = node[7]->getWhiteChildFront(_nodeDepth, _stack);
            if(COMMENT_RESULT) std::cout << "\tretornando: " << b << std::endl << std::endl;
            return b;
            break;
        }
    }
    if(COMMENT_RESULT) std::cout << "\tretornando: " << 0 << std::endl << std::endl;
    return 0;
}

void OctreeNode::rotate(int _rotateX, int _rotateY, int _rotateZ){

    if(type != GREY) return;
    if(_rotateX == R0 && _rotateY == R0 && _rotateZ == R0) return;

    OctreeNode *aux;
    switch(_rotateX){
        case R90:{
            //começando pelo lado esquerdo
            aux = node[0];
            node[0] = node[2];
            node[2] = node[6];
            node[6] = node[4];
            node[4] = aux;
            //lado direito
            aux = node[1];
            node[1] = node[3];
            node[3] = node[7];
            node[7] = node[5];
            node[5] = aux;

            break;
        }
        case R180:{
            //começando pelo lado esquerdo
            aux = node[0];
            node[0] = node[6];
            node[6] = aux;
            aux = node[2];
            node[2] = node[4];
            node[4] = aux;
            //lado direito
            aux = node[1];
            node[1] = node[7];
            node[7] = aux;
            aux = node[3];
            node[3] = node[5];
            node[5] = aux;

            break;
        }
        case R270:{
            //começando pelo lado esquerdo
            aux = node[0];
            node[0] = node[4];
            node[4] = node[6];
            node[6] = node[2];
            node[2] = aux;
            //lado direito
            aux = node[1];
            node[1] = node[5];
            node[5] = node[7];
            node[7] = node[3];
            node[3] = aux;

            break;
        }
    }

    switch(_rotateY){
        case R90:{
            //começando pela frente
            aux = node[0];
            node[0] = node[1];
            node[1] = node[3];
            node[3] = node[2];
            node[2] = aux;
            //lado de tras
            aux = node[4];
            node[4] = node[5];
            node[5] = node[7];
            node[7] = node[6];
            node[6] = aux;

            break;
        }
        case R180:{
            //começando pela frente
            aux = node[0];
            node[0] = node[3];
            node[3] = aux;
            aux = node[1];
            node[1] = node[2];
            node[2] = aux;
            //lado de tras
            aux = node[4];
            node[4] = node[7];
            node[7] = aux;
            aux = node[5];
            node[5] = node[6];
            node[6] = aux;

            break;
        }
        case R270:{
            //começando pela frente
            aux = node[0];
            node[0] = node[2];
            node[2] = node[3];
            node[3] = node[1];
            node[1] = aux;
            //lado de tras
            aux = node[4];
            node[4] = node[6];
            node[6] = node[7];
            node[7] = node[5];
            node[5] = aux;

            break;
        }
    }

    switch(_rotateZ){
        case R90:{
            //começando por baixo
            aux = node[0];
            node[0] = node[4];
            node[4] = node[5];
            node[5] = node[1];
            node[1] = aux;
            //cima
            aux = node[2];
            node[2] = node[6];
            node[6] = node[7];
            node[7] = node[3];
            node[3] = aux;

            break;
        }
        case R180:{
            //começando por baixo
            aux = node[0];
            node[0] = node[5];
            node[5] = aux;
            aux = node[1];
            node[1] = node[4];
            node[4] = aux;
            //cima
            aux = node[2];
            node[2] = node[7];
            node[7] = aux;
            aux = node[3];
            node[3] = node[6];
            node[6] = aux;

            break;
        }
        case R270:{
            //começando por baixo
            aux = node[0];
            node[0] = node[1];
            node[1] = node[5];
            node[5] = node[4];
            node[4] = aux;
            //cima
            aux = node[2];
            node[2] = node[3];
            node[3] = node[7];
            node[7] = node[6];
            node[6] = aux;

            break;
        }
    }

    //rearrumando os lugares dos nós ja que eles trocaram

    for(int i = 0; i < 8; i++){
        node[i]->setPosition(i);
        node[i]->rotate(_rotateX, _rotateY, _rotateZ);
    }
}

OctreeNode *combine(OctreeNode *_octree1, OctreeNode *_octree2, int _operation){
    switch(_operation){
        case UNION:
            return octreeNodeUnion(_octree1, _octree2);
        break;

        case INTERSECTION:
            return octreeNodeIntersection(_octree1, _octree2);
        break;

        case DIFFERENCE:
            return octreeNodeDifference(_octree1, _octree2);
        break;
    }
    std::cout <<"Combinação está retornando nulo" <<std::endl;
    return NULL;
}

OctreeNode *octreeNodeUnion(OctreeNode *_octree1, OctreeNode *_octree2, OctreeNode *_parent){
    unsigned int type1 = _octree1->getType();
    unsigned int type2 = _octree2->getType();

//    if(!_parent) std::cout <<"Sem pai" <<std::endl;
//    std::cout <<"Octree1: p: " << _octree1->getPosition() <<" | d: " <<_octree1->getDepth() << " | t: " << type1 <<std::endl;
//    std::cout <<"Octree2: p: " << _octree2->getPosition() <<" | d: " <<_octree2->getDepth() << " | t: " << type2 <<std::endl;

    if(type1 == BLACK){
        return new OctreeNode(*_octree1, _parent);
    }
    if(type2 == BLACK){
        return new OctreeNode(*_octree2, _parent);
    }
    if((type1 == WHITE) && (type2 == WHITE)){
        return new OctreeNode(*_octree1, _parent);
    }
    if((type1 == GREY) && (type2 == WHITE)){
        return new OctreeNode(*_octree1, _parent);
    }
    if((type1 == WHITE) && (type2 == GREY)){
        return new OctreeNode(*_octree2, _parent);
    }

//    std::cout <<"Tipo Grey Grey" <<std::endl;
    OctreeNode *result = new OctreeNode(_octree1->getDepth(), _octree1->getPosition(), _parent, GREY);
    for(int i = 0; i < 8; i++){
        result->setNode(i, octreeNodeUnion(_octree1->getNode(i), _octree2->getNode(i), result));
    }

    return result;
}

OctreeNode *octreeNodeIntersection(OctreeNode *_octree1, OctreeNode *_octree2, OctreeNode *_parent){
    unsigned int type1 = _octree1->getType();
    unsigned int type2 = _octree2->getType();

    if(type1 == WHITE)
        return new OctreeNode(*_octree1, _parent);
    if(type2 == WHITE)
        return new OctreeNode(*_octree2, _parent);
    if((type1 == BLACK) && (type2 == BLACK))
        return new OctreeNode(*_octree1, _parent);
    if((type1 == GREY) && (type2 == BLACK))
        return new OctreeNode(*_octree1, _parent);
    if((type1 == BLACK) && (type2 == GREY))
        return new OctreeNode(*_octree2, _parent);

    OctreeNode *result = new OctreeNode(_octree1->getDepth(), _octree1->getPosition(), _parent, GREY);
    for(int i = 0; i < 8; i++){
        result->setNode(i, octreeNodeIntersection(_octree1->getNode(i), _octree2->getNode(i), result));
    }

    return result;
}

OctreeNode *octreeNodeDifference(OctreeNode *_octree1, OctreeNode *_octree2, OctreeNode *_parent){
    unsigned int type1 = _octree1->getType();
    unsigned int type2 = _octree2->getType();

    if(type1 == WHITE)
        return new OctreeNode(*_octree1, _parent);
    if(type2 == BLACK)
        return new OctreeNode(_octree1->getDepth(), _octree1->getPosition(), _parent, WHITE);
    if(type2 == WHITE)
        return new OctreeNode(*_octree1, _parent);
    if((type1 == BLACK) && (type2 == GREY)){
        OctreeNode *result = new OctreeNode(*_octree2, _parent, true);
        return result;
    }

    OctreeNode *result = new OctreeNode(_octree1->getDepth(), _octree1->getPosition(), _parent, GREY);
    for(int i = 0; i < 8; i++){
        result->setNode(i, octreeNodeDifference(_octree1->getNode(i), _octree2->getNode(i), result));
    }

    return result;
}
